package vb.week2.tabular;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import vb.week2.tabular.Token.Kind;

public class Scanner {
	
	private char currentChar;
	private Kind currentKind;
    private int             currentLineNr = 0;
    private InputStream     in;
    private StringBuffer currentSpelling = null;

    private static final char cSPACE    = ' ',
                              cTAB      = '\t',
                              cEOLr     = '\r',
                              cEOLn     = '\n',
                              cPERCENT  = '%',
                              cEOT      = '\u0000',         /* End OF Text */
    						  cBSLASH   = '\u005C\u005C';
    // == '\\'  
    // We cannot use the '\\' denotation here because 
    // otherwise LaTeX's listings package will get very 
    // confused. Students should use '\\' of course.

   /** 
    * Constructor. 
    * @param in the stream from which the characters will be read
    */
    public Scanner(InputStream in) {    	
        this.in = in;  	
        currentChar = this.getNextChar();
        
    }                              
    
    /* Returns the next character. 
     * Returns cEOT when end-of-file or in case of an error. */
    private char getNextChar() {
        try {
            int ch = in.read();
            
            if (ch == -1) {
                ch = cEOT;
            } else if (ch == cEOLn) {
                currentLineNr++;
            }
            
            return (char)ch;
        } catch (IOException e) {
            return cEOT;
        }
    }
    
    public Token getToken() throws SyntaxErrorException{
    	currentSpelling = new StringBuffer("");
    	currentKind = scanToken();
    	return new Token(currentKind, currentSpelling.toString());
    }
    
    public static void main(String[] args){
    	FileInputStream is = null;
    	try {
			 is = new FileInputStream(new File("sample-3.tex"));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	Scanner scanner = new Scanner(is);
    	
    	Kind currentKind = null;
    	
    	while(currentKind != Token.Kind.EOT){
    		Token currentToken = null;
    		try {
				currentToken = scanner.getToken();
				currentKind = currentToken.getKind();
				String space = "";
				if(currentKind == Kind.AMPERSAND || currentKind == Kind.IDENTIFIER || currentKind == Kind.DOUBLE_BSLASH){
					space = "\t\t";
				} else{
					space = "\t\t\t";
				}
				System.out.println("" + currentToken.getKind().toString() + space + currentToken.getRepr());
			} catch (SyntaxErrorException e) {
				System.out.println("Syntax Error: " + e.getMessage());
				break;
			}
    		
    	}
    	
    	System.out.println("Aantal lines: " + scanner.getCurrentLineNumber());
    	
    	
    }
    
    public int getCurrentLineNumber(){
    	return this.currentLineNr;
    }
    

   /** 
    * Returns the next Token from the input.
    * @return the next Token 
    * @throws SyntaxErrorException when an unknown or unexpected character 
    *         has been found in the input. 
    */
    private Kind scanToken() throws SyntaxErrorException{
        
    	switch(currentChar){  	
    	
    	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':{
    		takeIt();
    		while(isLetter(currentChar) || isDigit(currentChar)){
    			takeIt();    			
    		}
    		return Token.Kind.IDENTIFIER;
    	}
    	
    	case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':{
    		takeIt();
    		while(isDigit(currentChar)){
    			takeIt();
    		}
    		return Token.Kind.NUM;
    	}
    	
    	case '\\':{
    		takeIt();
    		if(isBSlash(currentChar)){
    			takeIt();
    			return Token.Kind.DOUBLE_BSLASH;
    		} else{
    			return Token.Kind.BSLASH;
    		}
    		
    	}
    	
    	case '&':{
    		takeIt();
    		return Token.Kind.AMPERSAND;
    	}
    	
    	case '{':{
    		takeIt();
    		return Token.Kind.LCURLY;
    	}
    	
    	case '}':{
    		takeIt();
    		return Token.Kind.RCURLY;
    	}
    	
    	case cSPACE: case cTAB: case cEOLr: case cEOLn:{
    		ignore();
    		return this.scanToken();
    	}
    	
    	case cEOT:{
    		return Token.Kind.EOT;
    	}
    	
    	case cPERCENT:{
    		ignore(); //Niet zeker voor de volgende, dan komt de hele comment in de volgende string te staan. Dus hij moet daarna leeg zijn.
    		while(!isNextLineChar(currentChar)){
    			ignore();
    		}
    		ignore();
    		return this.scanToken();
    	}
    	
    	
    	default:{
    		throw new SyntaxErrorException("functie scanToken komt een char tegen die geen token kan vormen.");
    	}   	
    	
    	
    	}   	
    	
    }
    
    public void takeIt(){
    	currentSpelling.append(currentChar);
    	currentChar = this.getNextChar();
    }
    
    public void ignore(){
    	currentChar = this.getNextChar();
    }
    
    public boolean isLetter(char letter){
    	char[] letterArray = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};    	
    	for(int i = 0; i<letterArray.length; i++){
    		if(letter == letterArray[i]){
    			return true;
    		}
    	}
    	return false;
    }
    
    public boolean isDigit(char digit){
    	char[] digitArray = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};    	
    	for(int i = 0; i<digitArray.length; i++){
    		if(digit == digitArray[i]){
    			return true;
    		}
    	}
    	return false;
    }
    
    public boolean isBSlash(char ch){
    	return ch == '\\';
    }
    
    public boolean isNextLineChar(char ch){
    	return ch == cEOLn;
    }
    
    
 
}
